#include "task.hpp"

constexpr int alarm_message_len = 100;
constexpr int alarm_queue_len = 10;
std::deque<std::string> alarm_queue;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

struct alarm_message_in_file
{
    alarm_message_in_file()
    {
        // 初始化时就读取文件中的数据
        int fd = open(filename, O_RDONLY);
        if (fd < 0)
        {
            log(Fatal, "read %s open error, errno: %d, errstring: %s", filename, errno, strerror(errno));
            exit(-1);
        }

        int begin_pos = 0;
        off_t file_size = lseek(fd, 0, SEEK_END);
        if (file_size <= alarm_message_len * alarm_queue_len)
            begin_pos = -file_size;
        else
            begin_pos = -(alarm_message_len * alarm_queue_len);

        lseek(fd, begin_pos, SEEK_END);
        char buf[alarm_message_len] = { 0 };
        for (int i = 0; i < alarm_queue_len; ++i)
        {
            if (read(fd, buf, alarm_message_len) < 0)
                break;
            alarm_queue.push_back(buf);
        }
#ifdef __TEST_DEBUG__
        log(Info, "read over");
#endif
        write_fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
        if (write_fd < 0)
        {
            log(Fatal, "write %s open error, errno: %d, errstring: %s", filename, errno, strerror(errno));
            exit(-1);
        }
    }

    // 将最近一条报警信息写入文件
    void save_message(const std::string& message)
    {
        char buf[alarm_message_len] = { 0 };
        for (int i = 0; i < message.size(); ++i)
            buf[i] = message[i];
        write(write_fd, buf, alarm_message_len);
#ifdef __TEST_DEBUG__
        log(Info, "write over");
#endif
    }

    ~alarm_message_in_file()
    {
        close(write_fd);
    }

    const char* filename = "alarm_message.bin";
    int write_fd = -1;
}file_oper;

// 服务器获取报警信息
bool alarm_task(const std::string& message, const std::string& client_ip, uint16_t client_port)
{
    // 对传入message已作限制,长度不会超过50

    time_t t = time(nullptr);
    struct tm *ctime = localtime(&t);

    // 对获取的报警信息添加当前时间
    char buffer[alarm_message_len] = { 0 };
    snprintf(buffer, sizeof(buffer), "[%d-%02d-%02d %02d:%02d:%02d]:%s\n", \
                ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday, \
                ctime->tm_hour, ctime->tm_min, ctime->tm_sec, \
                message.c_str());
#ifdef __TEST_DEBUG__
    std::cout << message.c_str() << std::endl;
#endif

    // alarm_queue中仅存入最近10条信息
    pthread_mutex_lock(&mtx);
    if (alarm_queue.size() >= alarm_queue_len)
        alarm_queue.pop_front();
    alarm_queue.push_back(buffer);
    std::cout << alarm_queue.back() << std::endl;
    file_oper.save_message(alarm_queue.back());
    pthread_mutex_unlock(&mtx);

    return true;
}

struct http_response
{
    http_response()
    {
        // 构建http response报头
        http_text += "HTTP/1.0 200 OK\r\n";
        http_text += "Content-Type: text/html\r\n";
        // 固定大小
        http_text += "Content-Length: 13589";
        http_text += "\r\n\r\n";

        // 将html页面读入
        std::ifstream fin;
        fin.open("index.html", std::ifstream::in);
        std::string line;
        while (std::getline(fin, line))
        {
            int fpos = line.find("<!--write here-->");
            if (fpos != std::string::npos)
            {
                begin_pos = http_text.size();
                // 留够10个最近警告的空间
                for (int i = 0; i < 10; i++)
                    http_text += std::string((alarm_message_len + 12) * alarm_queue_len, ' ');   // 由于html格式 \t\t\t<h3>message</h3> ,故+12
                end_pos = http_text.size();
                continue;
            }
            http_text += line;
        }
        fin.close();
    }
    
    std::string http_text;  // http 响应报文
    size_t begin_pos;       // 报警信息写入位置
    size_t end_pos;         // 报警信息结束位置
} http_resp;

#include <sys/types.h>
#include <sys/socket.h>

// 远端获取报警信息函数
void feedback_task(int sockfd, const std::string client_ip, uint16_t client_port)
{
    while (true)
    {
        constexpr size_t recv_size = 1024;
        char inbuffer[recv_size] = { 0 };
        // ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
        ssize_t n = recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
        if (n > 0)
        {
            inbuffer[n] = 0;
#ifdef __TEST_DEBUG__
            std::cout << inbuffer << std::endl;
#endif
            std::string outbuffer;
            // 判断本次tcp请求是对应软件的请求还是其他
            // 其他请求一律返回http响应
            if (strcmp(inbuffer, "get") != 0)
            {
                // 将http response报头以及html前半部分内容写入
                outbuffer = http_resp.http_text.substr(0, http_resp.begin_pos);

                // 将报警信息作为html格式写入outbuffer中
                pthread_mutex_lock(&mtx);
                for (auto it = alarm_queue.begin(); it != alarm_queue.end(); ++it)
                {
                    char buffer[120] = { 0 };
                    snprintf(buffer, 120, "           <h3>%s</h3>", it->c_str());
                    outbuffer += buffer;
                }
                pthread_mutex_unlock(&mtx);
                
                // 将html后半部分内容写入
                outbuffer += http_resp.http_text.substr(http_resp.end_pos);
            }
            else
            {
                pthread_mutex_lock(&mtx);
                for (auto it = alarm_queue.begin(); it != alarm_queue.end(); ++it)
                    outbuffer += *it;
                pthread_mutex_unlock(&mtx);
            }
            
            // n = write(sockfd, outbuffer.c_str(), outbuffer.size());
            n = send(sockfd, outbuffer.c_str(), outbuffer.size(), 0);

            if (n < 0)
            {
                log(Warning, "write error, sockfd: %d, clientip: %s, clientport: %d, errno: %d, errstring: %s", sockfd, client_ip.c_str(), client_port, errno, strerror(errno));
                break;
            }
        }
        else if (n == 0)
        {
#ifdef __TEST_DEBUG__
            log(Info, "%s:%d quit, server close sockfd: %d", client_ip.c_str(), client_port, sockfd);
#endif
            break;
        }
        else
        {
            log(Warning, "read error, sockfd: %d, clientip: %s, clientport: %d, errno: %d, errstring: %s", sockfd, client_ip.c_str(), client_port, errno, strerror(errno));
            break;
        }
    }
    close(sockfd);
}